Frontend.ctrl.roster = function() {
	var store = {

	// table sorter
	}, _tablesorter = function() {
		var table = $('table', '#roster'),
			pager = $('div#pager', '#roster');

		// add the status sorter
	    $.tablesorter.addParser({
	    	id: 'status',
			type: 'numeric',
	        is: function(s) { return false; },
	        format: function(s) {
	            return s.replace(/online/, 3).replace(/busy/, 2).replace(/away/, 1).replace(/offline/, 0);
	        }
	    });

		// add the median sorter
	    $.tablesorter.addParser({
	    	id: 'median',
			type: 'numeric',
	        is: function(s) { return false; },
	        format: function(s) {
	            return s.toLowerCase().replace(/error \:\(/, 1).replace(/unknown/, 0);
	        }
	    });

		// make table sortable
	 	table.tablesorter({
	 		sortList: [[6,0]],
	        widgets: ['zebra'],
	 		headers: {1: {sorter: 'status'}, 3: {sorter: 'median'}},
		    textExtraction: {
				1: function(node) { return $(node).find("div.status").get(0).className.replace('status', ''); }
		    }

		// events
	    }).on('sortStart', function() {
	        Frontend.loading();
	    }).on('sortEnd', function() {
	        Frontend.finished();
 		}).on('pagerInitialized', function() {
 			var sel = pager.find('select'),
 				real = $('ul.pagination');

 			// hide old pager
 			pager.hide();

			// create li's
			sel.children().each(function(k, v) {
				k++;
				real.append('<li><a href="javascript:void(0);"' + (k == 1 ? ' class="active"' : '') + '>' + k + '</a></li>');
			});

			// event binder
			real.find('li').on('click', function() {
				var self = $(this);

				// make active
				real.find('li > a').removeClass('active');
				self.find('a').addClass('active');

				// simulate click
				sel.val(self.text()).change();
			});

		// add pager
	 	}).tablesorterPager({
	 		container: pager,
	 		size: 15
	 	});

	// binder
	}, bind = function() {
		// on item level update click
		$('a[data-method="update"]', '#roster').on('click', function() {
			var self = $(this),
				td = self.parent(),
				_error = function() {
					td.find('span').html('Error :(').removeAttr('style').css({color: 'red'});
					online(); // update table and take advantage of the online debounce
				};

			// hide link and show loading
			self.hide();
			td.append('<span style="cursor: wait;">Loading...</span>');

			// do blizzard request
			$.getJSON('http://eu.battle.net/api/wow/character/' + self.data('realm') + '/' + self.data('name') + '?fields=items&jsonp=?', function (data) {
				// send data home
				td.find('span').html('Working..');
				Frontend.ajax({type: 'post', data: {realm: self.data('realm'), name: self.data('name'), data: JSON.stringify(data)}, url: 'roster/update', success: function(data) {
					// update values (be fancy)
					td.find('span').html('Finished.').remove();
					self.html(data.ilevel).show();

					// update table and announce
					online(); // update table and take advantage of the online debounce
					Frontend.socket.announce({
						realm: self.data('realm'),
						name: self.data('name'),
						value: data.ilevel
					});
				}, error: _error});
			}).error(_error);
		});

		// on exhilarate update all :)
		$('button', 'form.roster_handlers').on('click', function(e) {
			e.preventDefault();
			var b = $(this);

			// exhilarate
			if(b.data('handle') == 'exhilarate')
				$('a[data-method="update"]:visible', '#roster').click();
		});

	// on online update (debounce it because holy fuck)
	}, online = function() {
		if(store.timer)
			window.clearTimeout(store.timer);

		// update table on online event
		store.timer = window.setTimeout(function() {
			$('table').trigger('update');
		}, 250);

	// on announce write and update
	}, announce = function(result) {
		$('a[data-method="update"][data-realm="'+ result.realm +'"][data-name="'+ result.name +'"]', '#roster').html(result.value);
		online(); // update table and take advantage of the online debounce

	// constructor
	}, __construct = function() {
		_tablesorter();
		bind();
	};

	// public, yay
	return {
		init: __construct,
		online: online,
		announce: announce
	};
}();